package com.cxb.carrecorder;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.util.Log;

public class VideoEncoder {
	private MediaCodec mMediaCodec;
	private int mWidth = 320;
	private int mHeight = 240;
	private int mFramerate = 15;
	private int mBitrate = 125000;

	private ByteBuffer[] mInputBuffers = null;
	private ByteBuffer[] mOutputBuffers = null;

	private MediaMuxer mMediaMuxer;
	private int mTrackIndex = -1;
	private boolean mMuxerStarted = false;

	private boolean mIsEncoding = false;
	private boolean mStopEncode = false;
	private long mFrameIndex = 0;

	public void initEncoder(String videoFile) {
		mMediaCodec = MediaCodec.createEncoderByType("video/avc");
		MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc",
				mWidth, mHeight);
		mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate);
		mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFramerate);
		mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
				MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
		mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
		mMediaCodec.configure(mediaFormat, null, null,
				MediaCodec.CONFIGURE_FLAG_ENCODE);
		mMediaCodec.start();

		try {
			mMediaMuxer = new MediaMuxer(videoFile,
					MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
		} catch (IOException ioe) {
			throw new RuntimeException("MediaMuxer creation failed", ioe);
		}

		mInputBuffers = mMediaCodec.getInputBuffers();
		mOutputBuffers = mMediaCodec.getOutputBuffers();
	}

	public VideoEncoder(int width, int height, int framerate, int bitrate) {
		mWidth = width;
		mHeight = height;
		mFramerate = framerate;
		mBitrate = bitrate;
		mIsEncoding = false;
		mStopEncode = false;
		mFrameIndex = 0;
		// beginTime = System.currentTimeMillis();
	}

	public void stopEncode() {
		mStopEncode = true;
		while (mIsEncoding) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		close();
	}

	public void startEncode(String videoFile) {
		initEncoder(videoFile);
		mFrameIndex = 0;
	}

	public void close() {
		try {
			if (mMediaCodec != null) {
				mMediaCodec.stop();
				mMediaMuxer.stop();
				mMediaCodec.release();
				mMediaMuxer.release();
				mMediaCodec = null;
				mMediaMuxer = null;
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void encode(byte[] srcData, boolean isrec) {
		if (mStopEncode == true) {
			mIsEncoding = false;
			return;
		}
		mIsEncoding = true;
		byte[] data = new byte[mWidth * mHeight * 3 / 2];
		// if(!isrec)
		nv21ToI420(srcData, data, mWidth, mHeight);
		// else
		// swapYV12toI420(srcData, data, mWidth, mHeight);
//		byte[] data=srcData;
		mInputBuffers = mMediaCodec.getInputBuffers();
		mOutputBuffers = mMediaCodec.getOutputBuffers();
		int inputBufferIndex = mMediaCodec.dequeueInputBuffer(0);
		if (inputBufferIndex >= 0) {
			ByteBuffer inputBuffer = mInputBuffers[inputBufferIndex];
			inputBuffer.clear();
			inputBuffer.put(data);
			long timestamp = mFrameIndex++ * 1000000 / mFramerate;
			mMediaCodec.queueInputBuffer(inputBufferIndex, 0, data.length,
					timestamp, 0);
		} else {
			mIsEncoding = false;
			return;
		}

		MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

		while (true) {
			int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo,
					0);
			if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
				break;
			} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
				mOutputBuffers = mMediaCodec.getOutputBuffers();
			} else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
				MediaFormat newFormat = mMediaCodec.getOutputFormat();
				mTrackIndex = mMediaMuxer.addTrack(newFormat);
				mMediaMuxer.start();
				mMuxerStarted = true;
			} else if (outputBufferIndex < 0) {

			} else {
				ByteBuffer outBuffer = mOutputBuffers[outputBufferIndex];
				if (outBuffer == null) {
					mIsEncoding = false;
					return;
				}

				if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
					bufferInfo.size = 0;
				}

				if (bufferInfo.size != 0) {

					if (!mMuxerStarted) {
						mIsEncoding = false;
						return;
					}
					outBuffer.position(bufferInfo.offset);
					outBuffer.limit(bufferInfo.offset + bufferInfo.size);
					mMediaMuxer.writeSampleData(mTrackIndex, outBuffer,
							bufferInfo);
				}

				mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);

				if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
					break;
			}
		}
		mIsEncoding = false;
	}

	static public void swapYV12toI420(byte[] yv12bytes, byte[] i420bytes,
			int width, int height) {
		System.arraycopy(yv12bytes, 0, i420bytes, 0, width * height);
		System.arraycopy(yv12bytes, width * height + width * height / 4,
				i420bytes, width * height, width * height / 4);
		System.arraycopy(yv12bytes, width * height, i420bytes, width * height
				+ width * height / 4, width * height / 4);
	}

	static public byte[] swapYV12toI4202(byte[] input, byte[] output,
			int width, int height) {
		/*
		 * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12 We convert by putting
		 * the corresponding U and V bytes together (interleaved).
		 */
		final int frameSize = width * height;
		final int qFrameSize = frameSize / 4;

		System.arraycopy(input, 0, output, 0, frameSize); // Y

		for (int i = 0; i < qFrameSize; i++) {
			output[frameSize + i * 2] = input[frameSize + i]; // Cb (U)
			output[frameSize + i * 2 + 1] = input[frameSize + i + qFrameSize]; // Cr
																				// (V)
		}
		return output;
	}

	public static byte[] nv21ToI420(byte[] data, byte[] output, int width,
			int height) {
		byte[] ret = output;
		int total = width * height;

		ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total);
		ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4);
		ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4);

		bufferY.put(data, 0, total);
		for (int i = total; i < data.length; i += 2) {
			bufferV.put(data[i]);
			bufferU.put(data[i + 1]);
		}

		return ret;
	}

	public static void getFrameJpegFile(String jpgFile, byte[] srcData,
			int format, int width, int height, float zoom, boolean isrec) {

		File imageFile = new File(jpgFile);
		BufferedOutputStream bos = null;
		try {
			bos = new BufferedOutputStream(new FileOutputStream(imageFile));

			byte[] data = new byte[width * height * 3 / 2];
			// if(!isrec)
			data = srcData;
			// else
			// swapYV12toI4202(srcData, data, width, height);
			YuvImage yuv = new YuvImage(data, format, width, height, null);

			ByteArrayOutputStream out = new ByteArrayOutputStream();
			yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
			byte[] bytes = out.toByteArray();
			BitmapFactory.Options opt = new BitmapFactory.Options();
			opt.inPreferredConfig = Bitmap.Config.RGB_565;
			opt.inPurgeable = true;
			opt.inInputShareable = true;
			Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
					bytes.length, opt);
			Matrix matrix = new Matrix();
			matrix.postScale(zoom, zoom);
			Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0,
					bitmap.getWidth(), bitmap.getHeight(), matrix, true);
			newBmp.compress(Bitmap.CompressFormat.JPEG, 80, bos);

			bos.flush();
			bos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static Bitmap drawStringToBitmap(Bitmap bitmap, String gText,
			int textsize) {
		android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();

		if (bitmapConfig == null) {
			bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
		}
		bitmap = bitmap.copy(bitmapConfig, true);

		Canvas canvas = new Canvas(bitmap);
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setColor(Color.RED);
		paint.setTextSize((int) (textsize));
		paint.setDither(true); // 闁兼儳鍢茶ぐ鍥╂崉閻斿摜顏搁柡鍛濞堟垿宕堕幆褍鍓奸梺鎻掓处閻楋拷
		paint.setFilterBitmap(true);// 閺夆晛娲﹂幎銈嗙▔閿熺晫鏄�
		Rect bounds = new Rect();
		paint.getTextBounds(gText, 0, gText.length(), bounds);
		int x = bitmap.getWidth() - bounds.width() - 20;
		int y = 50;
		canvas.drawText(gText, x, y, paint);
		return bitmap;
	}

	static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width,
			int height) {
		final int frameSize = width * height;

		for (int j = 0, yp = 0; j < height; j++) {
			int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
			for (int i = 0; i < width; i++, yp++) {
				int y = (0xff & ((int) yuv420sp[yp])) - 16;
				if (y < 0)
					y = 0;
				if ((i & 1) == 0) {
					v = (0xff & yuv420sp[uvp++]) - 128;
					u = (0xff & yuv420sp[uvp++]) - 128;
				}

				int y1192 = 1192 * y;
				int r = (y1192 + 1634 * v);
				int g = (y1192 - 833 * v - 400 * u);
				int b = (y1192 + 2066 * u);

				if (r < 0)
					r = 0;
				else if (r > 262143)
					r = 262143;
				if (g < 0)
					g = 0;
				else if (g > 262143)
					g = 262143;
				if (b < 0)
					b = 0;
				else if (b > 262143)
					b = 262143;

				rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000)
						| ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
			}
		}
	}

	public static void getFrameJpegFileWithTime(String jpgFile, byte[] srcData,
			int format, int width, int height, float zoom, boolean isrec) {

		File imageFile = new File(jpgFile);
		BufferedOutputStream bos = null;
		try {
			bos = new BufferedOutputStream(new FileOutputStream(imageFile));
//			byte[] data = new byte[width * height * 3 / 2];
//			if (!isrec)
//				data = srcData;
//			else
//				swapYV12toI4202(srcData, data, width, height);
			YuvImage yuv = new YuvImage(srcData, format, width, height, null);
			ByteArrayOutputStream stream = new ByteArrayOutputStream();
			yuv.compressToJpeg(new Rect(0, 0, width, height), 100, stream);
			Bitmap bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(),
					0, stream.size());
			stream.close();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
			bitmap = drawStringToBitmap(bitmap,
					sdf.format(System.currentTimeMillis()), 40);
			Matrix matrix = new Matrix();
			matrix.postScale(zoom, zoom);
			Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0,
					bitmap.getWidth(), bitmap.getHeight(), matrix, true);
			newBmp.compress(Bitmap.CompressFormat.JPEG, 80, bos);
			srcData = null;
			bitmap.recycle();
			bitmap = null;
			newBmp.recycle();
			newBmp = null;
			bos.flush();
			bos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public long getEncodeTime() {
		// return System.currentTimeMillis() - beginTime;
		return mFrameIndex * 1000 / this.mFramerate;
	}

	public boolean isEncodeComplete() {
		return mStopEncode;
	}
}